package com.dimples.system.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dimples.common.constant.DimplesConst;
import com.dimples.common.constant.StrPool;
import com.dimples.common.exception.DataException;
import com.dimples.system.entity.SysMenu;
import com.dimples.system.entity.SysRoleMenu;
import com.dimples.system.mapper.SysMenuMapper;
import com.dimples.system.service.SysMenuService;
import com.dimples.system.service.SysRoleMenuService;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

/**
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2021/6/25
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Resource
    private SysRoleMenuService roleMenuService;

    @Override
    public List<String> findUserPermissions(String username) {
        List<SysMenu> sysMenus = this.baseMapper.findUserPermissions(username);
        return sysMenus.stream()
                .filter(item -> StrUtil.isNotBlank(item.getPerms()) && !StrUtil.equalsIgnoreCase(item.getPerms(), StrPool.NULL))
                .map(SysMenu::getPerms).collect(Collectors.toList());
    }

    @Override
    public Object findMenuByUsername(String username) {
        List<SysMenu> sysMenus = this.baseMapper.findUserPermissions(username);
        return getTrees(sysMenus, DimplesConst.DEFAULT_PARENT);
    }

    private List<Tree<Long>> getTrees(List<SysMenu> sysMenus, Long defaultParent) {
        List<TreeNode<Long>> nodeList = CollUtil.newArrayList();

        sysMenus.forEach(item -> {
            TreeNode<Long> node = new TreeNode<>(item.getMenuId(), item.getParentId(), item.getMenuName(), item.getOrderNum());
            Map<String, Object> extra = CollUtil.newHashMap();
            extra.put("type", item.getType());
            extra.put("icon", item.getIcon());
            extra.put("href", item.getUrl());
            node.setExtra(extra);
            nodeList.add(node);
        });
        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名 都要默认值的
        treeNodeConfig.setIdKey("id");
        treeNodeConfig.setNameKey("title");
        treeNodeConfig.setParentIdKey("parentId");
        treeNodeConfig.setChildrenKey("children");
        treeNodeConfig.setWeightKey("order");
        // 最大递归深度
        treeNodeConfig.setDeep(3);
        return TreeUtil.build(nodeList, defaultParent, treeNodeConfig,
                (treeNode, tree) -> {
                    tree.setId(treeNode.getId());
                    tree.setParentId(treeNode.getParentId());
                    tree.setWeight(treeNode.getWeight());
                    tree.setName(treeNode.getName());
                    // 扩展属性 ...
                    tree.putAll(treeNode.getExtra());
                });
    }

    @Override
    public List<Tree<Long>> findMenuTree(boolean virtualNode) {
        List<SysMenu> sysMenus = this.list();
        List<SysMenu> menus = sysMenus.stream().sorted(Comparator.comparing(SysMenu::getOrderNum)).collect(Collectors.toList());
        long parentId = DimplesConst.DEFAULT_PARENT;
        if (virtualNode) {
            SysMenu basePower = new SysMenu();
            basePower.setMenuName("顶级菜单");
            basePower.setMenuId(0L);
            basePower.setParentId(-1L);
            basePower.setOrderNum(0L);
            menus.add(0, basePower);
            parentId = DimplesConst.DEFAULT_TOP;
        }
        return getTrees(menus, parentId);
    }

    @Override
    public List<Tree<Long>> findMenuTreeByRoleId(Long roleId) {
        List<SysMenu> sysMenus = this.baseMapper.findMenuByRoleId(roleId);
        return getTrees(sysMenus, DimplesConst.DEFAULT_PARENT);
    }

    @Override
    public List<SysMenu> findMenuByRoleId(Long roleId) {
        return this.baseMapper.findMenuByRoleId(roleId);
    }

    @Override
    public List<SysMenu> findMenu() {
        return this.list(Wrappers.lambdaQuery(SysMenu.class).orderByAsc(SysMenu::getOrderNum));
    }

    @Override
    public SysMenu findMenuById(Long menuId) {
        return this.getById(menuId);
    }

    @Override
    public void updateMenuById(SysMenu menu) {
        if (ObjectUtil.isNotEmpty(this.getById(menu.getMenuId()))) {
            menu.setModifyTime(new Date());
            this.updateById(menu);
        } else {
            throw new DataException(StrUtil.format("【menuId = {}】的菜单不存在", menu.getMenuId()));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteMenuById(Long menuId) {
        if (ObjectUtil.isNotEmpty(this.getById(menuId))) {
            // 判断是否是其他菜单的父级
            if (CollUtil.isNotEmpty(this.list(Wrappers.lambdaQuery(SysMenu.class).eq(SysMenu::getParentId, menuId)))) {
                throw new DataException(StrUtil.format("【menuId = {}】已被其他菜单引用，不能删除", menuId));
            }
            this.removeById(menuId);
            // 删除角色菜单关联关系
            this.roleMenuService.remove(Wrappers.lambdaUpdate(SysRoleMenu.class).eq(SysRoleMenu::getMenuId, menuId));
        } else {
            throw new DataException(StrUtil.format("【menuId = {}】的菜单不存在", menuId));
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteBatchMenuById(String menuIds) {
        List<Long> longs = CollUtil.newArrayList();
        for (long l : StrUtil.splitToLong(menuIds, StrPool.COMMA)) {
            longs.add(l);
        }
        for (Long menuId : longs) {
            if (ObjectUtil.isNotEmpty(this.getById(menuId))) {
                // 判断是否是其他菜单的父级
                List<Long> sysMenus = this.list(Wrappers.lambdaQuery(SysMenu.class).eq(SysMenu::getParentId, menuId))
                        .stream().map(SysMenu::getMenuId).collect(Collectors.toList());
                AtomicBoolean flag = new AtomicBoolean(false);
                for (Long id : sysMenus) {
                    if (CollUtil.contains(longs, id)) {
                        flag.set(true);
                    } else {
                        flag.set(false);
                        break;
                    }
                }
                if (CollUtil.isNotEmpty(sysMenus) && !flag.get()) {
                    throw new DataException(StrUtil.format("【menuId = {}】已被其他菜单引用，不能删除", menuId));
                }
                this.removeById(menuId);
                // 删除角色菜单关联关系
                this.roleMenuService.remove(Wrappers.lambdaUpdate(SysRoleMenu.class).eq(SysRoleMenu::getMenuId, menuId));
                flag.set(false);
            } else {
                throw new DataException(StrUtil.format("【menuId = {}】的菜单不存在", menuId));
            }
        }
    }

    @Override
    public void saveMenu(SysMenu sysMenu) {
        sysMenu.setCreateTime(new Date());
        if (StrUtil.isBlank(sysMenu.getType())) {
            sysMenu.setType(StrPool.ZERO);
        }
        if (ObjectUtil.isEmpty(sysMenu.getOrderNum())) {
            sysMenu.setOrderNum(DimplesConst.DEFAULT_PARENT);
        }
        this.save(sysMenu);
    }
}
